#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

#define DEBUG 1

typedef struct Cntl
{
    unsigned int TotalSize; // 存储空间总容量
    unsigned int UsedSize;  // 已使用空间
    unsigned int FreeSize;  // 空余空间

    char * EnterPtr ;       // 存储空间入口地址
    
}Cntl_t , *P_Cntl_t;


void ShowDebugData( P_Cntl_t Cntl )
{

    printf("当前总容量：%d\n" , Cntl->TotalSize );
    printf("当前已使用：%d\n" , Cntl->UsedSize );
    printf("当前空闲：%d\n" , Cntl->FreeSize );
    printf("入口地址：%p\n" , Cntl->EnterPtr );
    printf("当前数据：【%s】\n" , Cntl->EnterPtr);


}


P_Cntl_t ToolInit(unsigned int InitSize )
{
    // 申请管理结构体的内存空间
    P_Cntl_t cntl = calloc( 1 , sizeof(Cntl_t) );
    if( cntl == NULL )
    {
        perror("管理结构体申请失败..\n");
        return NULL ;
    }

    // 申请存储空间
    cntl->EnterPtr = calloc( InitSize , sizeof(char) );
    if( cntl->EnterPtr == NULL )
    {
        perror("存储空间申请失败..\n");
        return NULL ;
    }
    
    // 初始化其他参数
    cntl->TotalSize = InitSize ;
    cntl->UsedSize = 0 ;
    cntl->FreeSize = cntl->TotalSize ;

    // 返回管理结构体
    return cntl;
}


/// @brief 在现有数据末尾追加新的数据
/// @param Cntl 存储空间的管理结构体
/// @param NewMsg 需要追加的新数据
/// @return 返回成功追加的字节数
int append_data( P_Cntl_t Cntl , const char * NewMsg )
{

    // 1. 验货，检查新数据的尺寸
    int NewDataLen = strlen( NewMsg ) + 1;

    // 2. 检查仓库空余容量
    while (Cntl->FreeSize <= NewDataLen)
    {
        // 进行扩容
        Cntl->EnterPtr = realloc( Cntl->EnterPtr , Cntl->TotalSize * 2 ) ;
        Cntl->TotalSize *= 2 ;
        Cntl->FreeSize = Cntl->TotalSize - Cntl->UsedSize ;
    }
    
    // 3. 把新输入安放到原有数据的末尾
    int LastIndex = Cntl->UsedSize == 0 ? 0 : Cntl->UsedSize - 1; 
    memcpy( Cntl->EnterPtr + LastIndex , NewMsg , NewDataLen );

    // 4. 更新数据
    Cntl->UsedSize = LastIndex + NewDataLen ;
    Cntl->FreeSize = Cntl->TotalSize - Cntl->UsedSize ;
    
#if DEBUG
    printf("[Debug From File:%s Func:%s Line:%d]:\n" ,
                 __FILE__ , __FUNCTION__  , __LINE__ );
    ShowDebugData(Cntl);
#endif

    return NewDataLen ;
}


/// @brief 调整存储空间到最小
/// @param Cntl 管理结构体
/// @return 返回调整后的控件大小
int shrink_to_fit( P_Cntl_t Cntl )
{
    // 检查空闲空间是否超过总容量的一半
    if (Cntl->FreeSize >= Cntl->TotalSize/2 )
    {
        // 如果是则直接调整到最 fit 的状态
        Cntl->EnterPtr =  realloc( Cntl->EnterPtr , Cntl->UsedSize );
        Cntl->TotalSize = Cntl->UsedSize ;
        Cntl->FreeSize =  0 ;
    }

#if DEBUG
        printf("[Debug From File:%s Func:%s Line:%d]:\n" ,
                    __FILE__ , __FUNCTION__  , __LINE__ );
        ShowDebugData(Cntl);
#endif
    return Cntl->FreeSize ;
}

int main(int argc, char const *argv[])
{
    P_Cntl_t Cntl = ToolInit (16); // 初始化

    char NewMsg [32] = {0};
    int retVal = -1 ;

    while (1)
    {
        bzero(NewMsg , sizeof(NewMsg));
        scanf("%s" , NewMsg);   // 键盘获取任意长度数据

        switch (NewMsg[0]) // 检查追加还是调整内存请求
        {
            case '+':
                // 追加数据
                retVal = append_data( Cntl , NewMsg+1 );
                printf("成功添加%d字节数组..\n" , retVal );
                break;
            
            case '-' :
                retVal = shrink_to_fit(Cntl); // 调整内存尺寸
                printf("缩小后内存空间为：%d\n" , retVal ) ;
                break;
        }
    }
    
    return 0;
}

